`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/12/10 12:35:49
// Design Name:
// Module Name: CPU
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module CPU(
    input clk,
    input rst,
    input [15:0]inM,
    input [15:0]instruction,

    output [15:0]outM,
    output writeM,
    output [15:0]addressM,
    output [15:0] pc
  );

  logic [15:0] RegA, RegD; // A D 寄存�?
  logic [15:0] RegPC; // PC 寄存�?

  logic intr; // a/c 指令

  logic [15:0] aValue; // A 指令数据
  logic zx, nx, zy, ny, f, no, zr, ng;  // ALU
  logic [15:0] ALUIn2; // ALU Input 2, ALU Input 1 来自 RegD
  logic [15:0] ALUOutput; // ALU output

  logic dA, dD, dM; // d 指令
  logic jl, je, jg; // j 指令
  logic jmp;  // 根据 j 指令 �? ALU 输出�?认是否跳�?

  assign intr = instruction[15];    // 0 = A 指令, 1 = C 指令

  // A Value
  assign aValue = {1'b0, instruction[14:0]};

  // C 指令数据
  assign comp = intr & instruction[12];
  assign zx   = intr & instruction[11];
  assign nx   = intr & instruction[10];
  assign zy   = intr & instruction[9];
  assign ny   = intr & instruction[8];
  assign f    = intr & instruction[7];
  assign no   = intr & instruction[6];
  assign dA   = intr & instruction[5];
  assign dD   = intr & instruction[4];
  assign dM   = intr & instruction[3];
  assign jl   = intr & instruction[2];
  assign je   = intr & instruction[1];
  assign jg   = intr & instruction[0];

  always @(posedge clk)
  begin
    if (rst == 0)
    begin
      RegA <= 0;
      RegD <= 0;
      RegPC <= 0;
    end
    else
    begin
      if (intr==1 && dA == 1)
      begin
        RegA <= ALUOutput;
      end
      else if (intr == 0)
      begin
        RegA <= aValue;
      end
      else
      begin
        RegA <= RegA;
      end

      if (dD == 1)
      begin
        RegD <= ALUOutput;
      end
      else
      begin
        RegD <= RegD;
      end

      if (jmp == 1)
      begin
        RegPC <= RegA;
      end
      else
      begin
        RegPC <= RegPC + 1;
      end
    end
  end

  assign ALUIn2 = (comp == 1) ? inM : RegA;
  ALU alu(RegD, ALUIn2, zx, nx, zy, ny, f, no, ALUOutput, zr, ng);
  assign jmp = intr && (
           (je && (zr == 1))
           || (jl && (ng == 1))
           || (jg && (~(zr | ng))));

  assign outM = ALUOutput;
  assign writeM = dM;
  assign addressM = (intr == 0) ? aValue : RegA;  // A 指令，直接给出地址?
  assign pc = RegPC;

endmodule
